/*
 * encode.c
 *
 * CCITT ADPCM encoder
 *
 * Usage : encode [-3|4|5] [-a|u|l] < infile > outfile
 */
#include <stdio.h>
#include "g72x.h"


/*
 * Pack output codes into bytes and write them to stdout.
 * Returns 1 if there is residual output, else returns 0.
 */
int
pack_output(
        unsigned                code,
        int                     bits,
        FILE *                  fpout)
{
        static unsigned int     out_buffer = 0;
        static int              out_bits = 0;
        unsigned char           out_byte;

        out_buffer |= (code << out_bits);
        out_bits += bits;
        if (out_bits >= 8) {
                out_byte = out_buffer & 0xff;
                out_bits -= 8;
                out_buffer >>= 8;
                fwrite(&out_byte, sizeof (char), 1, fpout);
        }
        return (out_bits > 0);
}


main(
        int                     argc,
        char                    **argv)
{
        struct g72x_state       state;
        unsigned char           sample_char;
        short                   sample_short;
        unsigned char           code;
        int                     resid;
        int                     in_coding;
        int                     in_size;
        unsigned                *in_buf;
        int                     (*enc_routine)();
        int                     enc_bits;
        FILE                    *fpin, *fpout; 

        g72x_init_state(&state);

        /* Set defaults to u-law input, G.721 output */
        in_coding = AUDIO_ENCODING_ULAW;
        in_size = sizeof (char);
        in_buf = (unsigned *)&sample_char;
        enc_routine = g721_encoder;
        enc_bits = 4;

        /* Process encoding argument, if any */
        while ((argc > 1) && (argv[1][0] == '-')) {
                switch (argv[1][1]) {
                case '3':
                        enc_routine = g723_24_encoder;
                        enc_bits = 3;
                        break;
                case '4':
                        enc_routine = g721_encoder;
                        enc_bits = 4;
                        break;
                case '5':
                        enc_routine = g723_40_encoder;
                        enc_bits = 5;
                        break;
                case 'u':
                        in_coding = AUDIO_ENCODING_ULAW;
                        in_size = sizeof (char);
                        in_buf = (unsigned *)&sample_char;
                        break;
                case 'a':
                        in_coding = AUDIO_ENCODING_ALAW;
                        in_size = sizeof (char);
                        in_buf = (unsigned *)&sample_char;
                        break;
                case 'l':
                        in_coding = AUDIO_ENCODING_LINEAR;
                        in_size = sizeof (short);
                        in_buf = (unsigned *)&sample_short;
                        break;
                default:
fprintf(stderr, "CCITT ADPCM Encoder -- usage:\n");
fprintf(stderr, "\tencode [-3|4|5] [-a|u|l] < infile > outfile\n");
fprintf(stderr, "where:\n");
fprintf(stderr, "\t-3\tGenerate G.723 24kbps (3-bit) data\n");
fprintf(stderr, "\t-4\tGenerate G.721 32kbps (4-bit) data [default]\n");
fprintf(stderr, "\t-5\tGenerate G.723 40kbps (5-bit) data\n");
fprintf(stderr, "\t-a\tProcess 8-bit A-law input data\n");
fprintf(stderr, "\t-u\tProcess 8-bit u-law input data [default]\n");
fprintf(stderr, "\t-l\tProcess 16-bit linear PCM input data\n");
                        exit(1);
                }
                argc--;
                argv++;
        }

        fpin = NULL;
        fpout = NULL;

        if(argc > 1) fpin  = fopen(argv[1], "rb");
        argc--;
        argv++;
        if(argc > 1) fpout = fopen(argv[1], "wb");
        argc--;
        argv++;

        if(fpin  == NULL) fpin  = stdin;
        if(fpout == NULL) fpout = stdout;

        /* Read input file and process */
        while (fread(in_buf, in_size, 1, fpin) == 1) {
                code = (*enc_routine)(in_size == 2 ? sample_short : sample_char,
                    in_coding, &state);
                resid = pack_output(code, enc_bits, fpout);
        }

        /* Write zero codes until all residual codes are written out */
        while (resid) {
                resid = pack_output(0, enc_bits, fpout);
        }
        
        if(fpin  != NULL && fpin  != stdin)  fclose(fpin);
        if(fpout != NULL && fpout != stdout) fclose(fpout);
}
